////////////////////////////////////////////////
// DESCRIPTION:
//    OAL Interface Wrappers for FusionWare.DFX
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#pragma once

#include <CEDDK.h>
#include <FusionWare\IoControlCodes.h>
#include <ddkreg.h>

namespace FusionWare 
{
    namespace DFX
    {
        // Group = Common Driver Library

        // enum to identify the mode for an interrupt
        enum IntMode
        {
            None,               // no special mode needed (disabled)
            EdgeTriggered,      // Edge Triggered (State indicates rising or falling)
            LevelTriggered,     // Level Triggered (State indicates Active High or Low)
        };

        // Data passed as input buffer in BUSDATA_PARAMS to IOCTL_HAL_DRVLIB_ENABLE_INT
        struct EnableIntData
        {
            enum IntMode Mode;
            int State;         // Rising(1)/Falling(0) for edge or High(1)/low(0) for Level 
                               // or BothEdges(3) for edge
            DWORD DevIRQ;      // Identifier for the int (SYSINTR or SYSWAKE value)
        };
      

        // Set the IRQ mode for the specified interrupt source
        inline bool SetIrqMode( const EnableIntData& IntData)
        {
            // check for null input - OK it's a NOP.
                if(IntData.Mode == None && IntData.DevIRQ == IRQ_UNSPECIFIED)
                return TRUE;

            DWORD dwBytesReturned;
            return  0!=KernelIoControl( IOCTL_HAL_SETINTMODE
                                   , const_cast<EnableIntData*>(&IntData)
                                   , sizeof(EnableIntData)
                                   , NULL
                                   , 0
                                   , &dwBytesReturned);
        }
        
        struct ResourceAddress 
        {
            INTERFACE_TYPE BusType;
            UINT32 BusNumber;
            UINT32 SlotNumber;
            PHYSICAL_ADDRESS PhysAddr;
            bool InIoSpace;
            ULONG WindowSize;
        };
        
        // Set the IRQ mode for the specified interrupt source
        inline bool IsResourceAvailable( const ResourceAddress& Resource)
        {
            DWORD dwBytesReturned;
            BOOL IsAvailable = FALSE;
            KernelIoControl( IOCTL_RES_AVAILABLE
                                   , const_cast<ResourceAddress*>(&Resource)
                                   , sizeof(Resource)
                                   , &IsAvailable
                                   , sizeof IsAvailable
                                   , &dwBytesReturned);
                                   
            return IsAvailable>0;
        }

        ///////////////////////////////////////////////////////
        // Description: 
        //    Requests a SYINTR for a specified IRQ
        //
        // Input: 
        //    Irq      - IRQ to request a SYSINTR for
        //    pSysIntr - pointer to new SYSINTR value
        //    Flags    - Flags for ResourceRequestEx
        //
        // Remarks: 
        //     This function first requests the IRQ from the I/O resource manager
        //     then requests the OAL to assign a SYSINTR for it.
        //     The resource manager registry entries must be configured to indicated
        //     that an IRQ is shared for multiple requests of the same IRQ to succeed
        //
        bool RequestSysIntr(DWORD Irq, DWORD* pSysIntr, DWORD Flags = 0);
        

        ///////////////////////////////////////////////////////
        // Description: 
        // 
        // Input: 
        //    SysIntr - SYSINTR retreaved from RequestSysIntr
        //
        bool ReleaseSysIntr(DWORD SysIntr);


        ///////////////////////////////////////////////////////
        // Description: 
        //    Sets the mode of operation for the interrupt source
        //
        // Input: 
        //    BusDataType - The bus type for the Interrupt
        //    BusNumber   - Bus instance number
        //    SlotNumber  - Slot number on the bus
        //    Mode        - EdgeTriggered or LevelTriggered
        //    State       - TRUE = Active HIGH; FALSE = Active LOW
        //
        // Remarks: 
        //     
        inline bool SetIrqMode( enum IntMode Mode, int State, DWORD IRQID )
        {
            const EnableIntData intData = {Mode, State, IRQID};
            return SetIrqMode(intData);
        }


        // Typesafe Template for allocating CPU Device registers in Device Drivers and
        // other user mode code.
    
        ///////////////////////////////////////////////////////
        // Description: 
        //    Allocates virtual space for a physical device.
        //    using a FULL physical address.
        //
        // Input: 
        //    T          - Type to use for determining size of allocation
        //                 nomrally this is a structor class where each member 
        //                 is a register in the device and the layout of the struct
        //                 matches the register layout on the device.
        //
        //    PhysAddr   - Physical Address of the device (as a DWORD)
        //    BusType    - Bus Type for the Bus the device is attached to
        //                 Default [ProcessorInternal]
        //
        //    BusNum     - Bus instance Number [Default = 0 ]
        //    InIoSpace  - Flag to indicate if the address is in I/O space [Default = FALSE]
        //
        // Returns: 
        //     T* 
        //     
        // Remarks: 
        //     Allocates device register access in the virtual space of the calling process
        //     if pInIoSpace is NULL (the default) then Memory mapped is assumed.
        //     
        // Example:
        //    FusionWare::PXA250::UART* pUART = AllocDeviceRegs(UART_BASE_PHYSICAL); 
        //
        template <class T>
        inline T* AllocDeviceRegs(PHYSICAL_ADDRESS PhysAddr,INTERFACE_TYPE BusType=ProcessorInternal,int BusNum = 0, BOOL InIoSpace=FALSE)
        {
            ASSERT(sizeof(BOOL)==sizeof(DWORD));
            void* retVal=NULL;

            if(!TransBusAddrToVirtual(BusType,BusNum,PhysAddr,sizeof(T),(DWORD*)&InIoSpace,&retVal))
               return NULL;
            
            return (T*)retVal;
        }

        ///////////////////////////////////////////////////////
        // Description: 
        //    Allocates virtual space for a physical device.
        //    using a DWORD for the physical address.
        //
        // Input: 
        //    T          - Type to use for determining size of allocation
        //                 nomrally this is a structor class where each member 
        //                 is a register in the device and the layout of the struct
        //                 matches the register layout on the device.
        //
        //    Addr       - Physical Address of the device
        //    BusType    - Bus Type for the Bus the device is attached to
        //                 Default [ProcessorInternal]
        //
        //    BusNum     - Bus instance Number [Default = 0 ]
        //    InIoSpace  - Flag to indicate if the address is in I/O space [Default = FALSE]
        //
        // Returns: 
        //     T* 
        //     
        // Remarks: 
        //     Allocates device register access in the virtual space of the calling process
        //     if pInIoSpace is NULL (the default) then Memory mapped is assumed.
        //     
        // Example:
        //    FusionWare::Au1500::UART* pUART = AllocDeviceRegs(UART_BASE_PHYSICAL); 
        //
        // Note:
        //  This version is deprecated as it does not support the CPUs with physical addreses
        //  having greater than 32 bits. (e.g. many MIPS implementatiosn have a 36 bit physical
        //  address)
        //
        template <class T>
        inline T* AllocDeviceRegs(DWORD Addr, INTERFACE_TYPE BusType=ProcessorInternal, int BusNum = 0, BOOL InIoSpace=FALSE)
        {
            PHYSICAL_ADDRESS addr = {Addr, 0};
        
            return AllocDeviceRegs<T>(addr, BusType, BusNum, InIoSpace);
        }

        ///////////////////////////////////////////////////////
        // Description: 
        //    Allocates virtual space for a physical device.
        //    using a UINT_PTR for the physical address.
        //
        // Input: 
        //    T          - Type to use for determining size of allocation
        //                 nomrally this is a structor class where each member 
        //                 is a register in the device and the layout of the struct
        //                 matches the register layout on the device.
        //
        //    Addr       - Physical Address of the device
        //    BusType    - Bus Type for the Bus the device is attached to
        //                 Default [ProcessorInternal]
        //
        //    BusNum     - Bus instance Number [Default = 0 ]
        //    InIoSpace  - Flag to indicate if the address is in I/O space [Default = FALSE]
        //
        // Returns: 
        //     T* 
        //     
        // Remarks: 
        //     Allocates device register access in the virtual space of the calling process
        //     if pInIoSpace is NULL (the default) then Memory mapped is assumed.
        //     
        // Example:
        //    FusionWare::Au1500::UART* pUART = AllocDeviceRegs(UART_BASE_PHYSICAL); 
        //
        // Note:
        //  This version is deprecated as it does not support the CPUs with physical addreses
        //  having greater than 32 bits. (e.g. many MIPS implementatiosn have a 36 bit physical
        //  address)
        //
        template <class T>
        inline T* AllocDeviceRegs(UINT_PTR Addr, INTERFACE_TYPE BusType=ProcessorInternal, int BusNum = 0, BOOL InIoSpace=FALSE)
        {
            PHYSICAL_ADDRESS physAddr = {Addr, 0};
        
            return AllocDeviceRegs<T>(physAddr, BusType, BusNum, InIoSpace);
        }

        ///////////////////////////////////////////////////////
        // Description: 
        //    Allocates virtual space for a physical device.
        //    using a device register struc pointer for the physical address.
        //
        // Input: 
        //    T          - Type to use for determining size of allocation
        //                 nomrally this is a structor class where each member 
        //                 is a register in the device and the layout of the struct
        //                 matches the register layout on the device.
        //
        //    PhysAddr   - pointer containing physical address of the device
        //    BusType    - Bus Type for the Bus the device is attached to
        //                 Default [ProcessorInternal]
        //
        //    BusNum     - Bus instance Number [Default = 0 ]
        //    InIoSpace  - Flag to indicate if the address is in I/O space [Default = FALSE]
        //
        // Returns: 
        //     T* 
        //     
        // Remarks: 
        //     Allocates device register access in the virtual space of the calling process
        //     if pInIoSpace is NULL (the default) then Memory mapped is assumed.
        //     
        // Example:
        //    FusionWare::Au1500::UART* pUART = AllocDeviceRegs(UART_BASE_PHYSICAL); 
        //
        // Note:
        //  This version is typically used with on-chip devices and the FusionWare defined
        //  physical address pointers. It is
        //
        template <class T>
        inline T* AllocDeviceRegs(T* const PhysAddr, INTERFACE_TYPE BusType=ProcessorInternal, int BusNum = 0, BOOL InIoSpace=FALSE)
        {
            return AllocDeviceRegs<T>((UINT_PTR)PhysAddr, BusType, BusNum, InIoSpace);
        }

        // Releases the allocation from AllocDeviceRegs or AllocDeviceRegsPA
        //
        //Remarks:
        //  OK to call for I/O Mapped ranges. Thus this should always be called
        //
        // Implementation Notes:
        //   + MmUnMapIoSpace will fail for invalid adresses
        //   + The x86 I/O space is only 64K [0x0000 - 0xFFFF]
        //   + Windows CE treats the first 64K of Virtual address as
        //     invalid for NULL pointer checks.
        //
        //   Therefore, calling this on an I/O mapped device address will
        //   not cause harm. Errors will be generated that are silently ignored.
        //
        template <class T>
        inline void FreeDeviceRegs(T* pDev)
        {
            // SEH probably not needed here but be safe anyway.
            __try {
                if(((UINT_PTR)pDev) <= 0xFFFF)
                {
                    // compiler will catch accidental use of this template on const.
                    // If you get compiler error C2440 here then you need to verify
                    // that freeing the pointer is really correct and if so
                    // call with the non-const pointer.
                    // E.g. FreeDeviceRegs(const_cast<GPIO*>(pReadOnlyGPIO));
                    MmUnmapIoSpace(reinterpret_cast<void*>(pDev), sizeof(*pDev));
                }
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {}
        }

    }
}

